﻿#if defined _MSC_VER
	#define _CRT_SECURE_NO_WARNINGS
#endif
#include "Y2Y.h"


int64_t xor_mem(char* dest, const char* src, int64_t src_len, const char* key, int16_t len)
{
	int16_t total_len = 0;
	while (total_len < src_len)
		for (int i = 0; i < len; ++i) {
			*dest++ = *src++ ^ key[i];
			++total_len;
			if (total_len == src_len) {
				return total_len;
			}
		}
	return total_len; // 理论上不会到这
}

char y2_xor_mem(y2yCtx* const ctx, char* dest, const char* src, int16_t len)
{
	if (!ctx || !ctx->key_content_ || ctx->key_len_ <= 0 || !dest || !src || len <= 0)
		return ERR_PARAMETER;
	//if (len < strlen(dest))
	//	dest[len] = '\0';
	int16_t total_len = 0;
	while (total_len < len)
		for (int i = 0; i < ctx->key_len_; ++i) {
			*dest++ = *src++ ^ ctx->key_content_[i];
			++total_len;
			if (total_len == len) {
				ctx->count_xor_ += total_len;
				return ERR_NO;
			}
		}
	ctx->count_xor_ += total_len; // 理论上不会到这
	return ERR_NO;
}

void y2_error_msg(char* buffer, char err_code)
{
	if (buffer == NULL)
		return;
	switch (err_code) {
		case ERR_LENGTH_OVER_KEY: // 1
			memcpy(buffer, "Length is greater than key length", 34);
			break;
		case ERR_NO: // 0
			memcpy(buffer, "No error", 9);
			break;
		case ERR_MEM_ALLOC: // -1
			memcpy(buffer, "Memory allocation error", 24);
			break;
		case ERR_FILE_OPEN: // -2
			memcpy(buffer, "File open error", 16);
			break;
		case ERR_FILE_READ: // -3
			memcpy(buffer, "File read error", 16);
			break;
		case ERR_FILE_WRITE: // -4
			memcpy(buffer, "File write error", 17);
			break;
		case ERR_FILE_CLOSE: // -5
			memcpy(buffer, "File close error", 17);
			break;
		case ERR_FILE_EMPTY: // -6
			memcpy(buffer, "File is empty", 14);
			break;
		case ERR_FILE_SEEK: // -7
			memcpy(buffer, "File seek error", 16);
			break;
		case ERR_FILE_TELL: // -8
			memcpy(buffer, "File tell error", 16);
			break;
		case ERR_FILE_NOT_FOUND: // -9
			memcpy(buffer, "File not found", 15);
			break;
		case ERR_FILE_MODE: // -10
			memcpy(buffer, "File open mode error", 21);
			break;
		case ERR_FILE_POS: // -11
			memcpy(buffer, "File position error", 20);
			break;
		case ERR_PARAMETER: // -12
			memcpy(buffer, "Parameter error", 16);
			break;
		default:
			memcpy(buffer, "Unknown error", 14);
			break;
	}
}

void init_y2yCtx(y2yCtx* const ctx) {
	//ctx->enc_dec_content_ = NULL;
	//ctx->temp_content_ = NULL;
	ctx->key_content_ = NULL;
	ctx->key_len_ = 0;
	ctx->count_xor_ = 0;
	//return ERR_NO;
}

void free_y2yCtx(y2yCtx* const ctx) {
	//if (ctx->enc_dec_content_)
	//	free(ctx->enc_dec_content_);
	//ctx->enc_dec_content_ = NULL;
	//if (ctx->temp_content_)
	//	free(ctx->temp_content_);
	//ctx->temp_content_ = NULL;
	if (ctx->key_content_)
		free(ctx->key_content_);
	ctx->key_content_ = NULL;
	ctx->key_len_ = 0;
	ctx->count_xor_ = 0;
}

void reset_y2yCtx_count(y2yCtx* const ctx) {
	if (!ctx)
		return;
	ctx->count_xor_ = 0;
}

char y2_load_file_key(y2yCtx* const ctx, const char* key_filename, uint16_t len) {
	FILE* fp = fopen(key_filename, "rb");
	if (fp == NULL)
		return ERR_FILE_OPEN;
	int seek_ret = fseeko(fp, 0, SEEK_END);
	if (seek_ret != 0) {
		fclose(fp);
		return ERR_FILE_SEEK;
	}
	long long file_len = ftello(fp);
	if (file_len == 0) {
		ctx->key_content_ = 0;
		ctx->key_len_ = 0;
		fclose(fp);
		return ERR_FILE_EMPTY;
	}
	else if (file_len < 0) {
		ctx->key_content_ = 0;
		ctx->key_len_ = 0;
		fclose(fp);
		return ERR_FILE_TELL;
	}
	/*static */char too_length_flag = ERR_NO;// 静态单例变量存在多线程资源竞争隐患
	if (file_len < len || len == 0) {
		if (file_len < len) // 大于文件长度
			too_length_flag = ERR_LENGTH_OVER_KEY;
		// 使得 key_len_ 等于文件长度
		ctx->key_len_ = len = file_len;
	}
	seek_ret = fseeko(fp, 0, SEEK_SET);
	if (seek_ret != 0) {
		fclose(fp);
		return ERR_FILE_SEEK;
	}
	// 分配 key_content_ 内存
	char* key_buffer = (char*)malloc(len + 1);
	if (key_buffer == NULL) {
		ctx->key_content_ = NULL;
		ctx->key_len_ = 0;
		fclose(fp);
		return ERR_MEM_ALLOC;
	}
	fread(key_buffer, 1, len, fp);
	key_buffer[len] = '\0';
	ctx->key_len_ = len;
	if (ctx->key_content_) {
		free(ctx->key_content_);
		//ctx->key_content_ = NULL;
	}
	ctx->key_content_ = key_buffer;
	int close_ret = fclose(fp);
	if (close_ret != 0)
		return ERR_FILE_CLOSE;
	return too_length_flag = ERR_NO ? ERR_NO : ERR_LENGTH_OVER_KEY; // 反正返回值就两个
}

char y2_set_key_content(y2yCtx* const ctx, const char* key_content, int16_t len) {
	if (!key_content || !ctx)
		return ERR_PARAMETER;
	uint16_t key_len = strlen(key_content);
	if (len == 0 || len > key_len)
		len = key_len;
	char* key_buffer = (char*)malloc(len + 1);
	if (key_buffer == NULL) {
		ctx->key_len_ = 0;
		return ERR_MEM_ALLOC;
	}
	ctx->key_len_ = len;
	strncpy(key_buffer, key_content, len);
	key_buffer[len] = '\0';
	if (ctx->key_content_) {
		free(ctx->key_content_);
		//ctx->key_content_ = NULL;
	}
	ctx->key_content_ = key_buffer;
	return ERR_NO;
}

char y2_codec_file_to_file(y2yCtx* const ctx,
	const char* dest_filename,
	const char* mode,
	const char* src_filename,
	int64_t start_pos,
	int64_t len)
{
	if (!ctx || !ctx->key_content_ || (ctx->key_len_ ==0) || !dest_filename || !src_filename)
		return ERR_PARAMETER;
	if (mode == 'r')
		return ERR_FILE_MODE;

	// 确保以二进制模式打开文件
	char save_mode[5];
	save_mode[0] = mode[0];
	if (mode[1] == 'b' || mode[1] == 0) {
		save_mode[1] = 'b';
		save_mode[2] = 0;
	}
	else if (mode[1] == '+') {
		if (*mode == 'w') {
			save_mode[1] = 'b';
			save_mode[2] = '+';
			save_mode[3] = 0;
		}
		else {
			save_mode[1] = '+';
			save_mode[2] = 'b';
			save_mode[3] = 0;
		}
	}
	else
		return ERR_FILE_MODE;

	FILE* save_fp = fopen(dest_filename, save_mode);
	if (save_fp == NULL)
		return ERR_FILE_OPEN;
	FILE* src_fp = fopen(src_filename, "rb");
	if (src_fp == NULL) {
		fclose(save_fp);
		return ERR_FILE_OPEN;
	}
	int seek_ret = fseeko(src_fp, 0, SEEK_END);
	if (seek_ret != 0) {
		fclose(src_fp);
		fclose(save_fp);
		return ERR_FILE_EMPTY;
	}
	else if (start_pos < 0) {
		fclose(src_fp);
		fclose(save_fp);
		return ERR_FILE_SEEK;
	}
	long long end_pos = ftello(src_fp);
	if (end_pos == 0) {
		fclose(src_fp);
		fclose(save_fp);
		return ERR_FILE_EMPTY;
	}
	if (start_pos >= end_pos) {
		fclose(src_fp);
		fclose(save_fp);
		return ERR_FILE_POS;
	}
	if (len == 0 || len > end_pos - start_pos) { // 长度为 0 则读取整个文件
		seek_ret = fseeko(src_fp, start_pos, SEEK_SET);
		if (seek_ret != 0) {
			fclose(src_fp);
			fclose(save_fp);
			return ERR_FILE_SEEK;
		}
		len = end_pos - start_pos;
	}

	int64_t total_len = 0;
	while (total_len < len) {
		for (int i = 0; i < ctx->key_len_/* && total_len < len*/; ++i) {
			unsigned char c = fgetc(src_fp);
			++total_len;
			fputc(c ^ (unsigned char)ctx->key_content_[i], save_fp);
			if (total_len == len)
				break;
		}
		if (total_len == len || feof(src_fp)) {
			// 如果已经读取了足够的字符或者文件结束，跳出外层循环
			break;
		}
	}
	ctx->count_xor_ += total_len;
	fclose(src_fp);
	fclose(save_fp);
	return ERR_NO;
}

char y2_codec_file_to_mem(y2yCtx* const ctx,
	char* dest,
	int64_t dest_len,
	const char* src_filename,
	int64_t start_pos)
{
	if (!ctx || !ctx->key_content_ || (ctx->key_len_ == 0) || !dest || dest_len < 0 || !src_filename || start_pos < 0)
		return ERR_PARAMETER;
	//if (dest_len == 0) { // 等于 0 则读取整个 dest 长度
	//	dest_len = strlen(dest) + 1;
	//	if (dest_len == 1)
	//		return ERR_PARAMETER;
	//}
	FILE* src_fp = fopen(src_filename, "rb");
	if (src_fp == NULL)
		return ERR_FILE_OPEN;
	int seek_ret = fseeko(src_fp, 0, SEEK_END);
	if (seek_ret != 0) {
		fclose(src_fp);
		return ERR_FILE_EMPTY;
	}
	else if (seek_ret < 0) {
		fclose(src_fp);
		return ERR_FILE_SEEK;
	}
	long long end_pos = ftello(src_fp);
	if (end_pos == 0) {
		fclose(src_fp);
		return ERR_FILE_EMPTY;
	}
	else if (end_pos < 0) {
		fclose(src_fp);
		return ERR_FILE_TELL;
	}
	if (start_pos >= end_pos) {
		fclose(src_fp);
		return ERR_FILE_POS;
	}
	seek_ret = fseeko(src_fp, start_pos, SEEK_SET);
	if (seek_ret != 0) {
		fclose(src_fp);
		return ERR_FILE_SEEK;
	}
	int64_t total_len = 0;
	int64_t remain = end_pos - start_pos;
	while (total_len < dest_len) {
		for (int i = 0; i < ctx->key_len_/* && total_len < dest_len*/; ++i) {
			unsigned char c = fgetc(src_fp);
			++total_len;
			dest[total_len - 1] = c ^ (unsigned char)ctx->key_content_[i];
			if ((total_len == dest_len) || (total_len == remain))
				break;
		}
		if ((total_len == dest_len) || feof(src_fp) || (total_len >= remain)) {
			// 如果已经读取了足够的字符或者文件结束或者已经读取了剩余的所有字符，跳出外层循环
			if (total_len < dest_len)
				dest[total_len] = '\0';
			break;
		}
	}
	ctx->count_xor_ += total_len;
	fclose(src_fp);
	return ERR_NO;
}

char y2_codec_mem_to_file(y2yCtx* const ctx,
	const char* dest_filename,
	const char* mode,
	const char* src,
	int64_t src_len)
{
	if (!ctx || !ctx->key_content_ || (ctx->key_len_ == 0) || !dest_filename || !src || src_len <= 0)
		return ERR_PARAMETER;
	if (mode == 'r')
		return ERR_FILE_MODE;

	// 确保以二进制模式打开文件
	char save_mode[5];
	save_mode[0] = mode[0];
	if (mode[1] == 'b' || mode[1] == 0) {
		save_mode[1] = 'b';
		save_mode[2] = 0;
	}
	else if (mode[1] == '+') {
		if (*mode == 'w') {
			save_mode[1] = 'b';
			save_mode[2] = '+';
			save_mode[3] = 0;
		}
		else {
			save_mode[1] = '+';
			save_mode[2] = 'b';
			save_mode[3] = 0;
		}
	}
	else
		return ERR_FILE_MODE;

	FILE* save_fp = fopen(dest_filename, save_mode);
	if (save_fp == NULL)
		return ERR_FILE_OPEN;

	int64_t total_len = 0;
	while (total_len < src_len) {
		for (int i = 0; i < ctx->key_len_/* && total_len < src_len*/; ++i) {
			unsigned char c = src[total_len++];
			fputc(c ^ (unsigned char)ctx->key_content_[i], save_fp);
			if (total_len == src_len)
				break;
		}
	}
	ctx->count_xor_ += total_len;
	fclose(save_fp);
	return ERR_NO;
}
